#include "matrix.h"





//constructeur
//cree une matrice nul
Matrix::Matrix(int numRow_,int numCol_)
{
	numRow=numRow_;
	numCol=numCol_;
	
	tab = new double*[numRow];
	for(int i=0;i<numRow;i++)
	{
		tab[i] = new double[numCol];
	}

	for(int i=0;i<numRow;i++)
	{
		for(int j=0;j<numCol;j++)
		{
			tab[i][j]=0;
		}
	}

}


//change les valeurs
// -> pas de test sur les dimensions des matrices et des donnees
void Matrix::fill( ... )
{
	va_list vl;
	va_start(vl,numRow*numCol);

	for(int i=0;i<numRow;i++)
	{
		for(int j=0;j<numCol;j++)
		{
			tab[i][j]=va_arg(vl,double);
		}
	}
	va_end(vl);
}


//destructeur
Matrix::~Matrix()
{
	for(int i=0;i<numRow;i++)
	{
		delete [] tab[i];
	}

	delete [] tab;
}


//constructeur par copie
Matrix::Matrix(const Matrix &MatrixToCopy)
{
	numRow=MatrixToCopy.numRow;
	numCol=MatrixToCopy.numCol;

	tab = new double*[numRow];

	for(int i=0;i<numRow;i++)
	{
		tab[i] = new double[numCol];
	}

	for(int i=0;i<numRow;i++)
	{
		for(int j=0;j<numCol;j++)
		{
			tab[i][j]=MatrixToCopy[i][j];
		}
	}
}


//surchage de l'operateur +
Matrix Matrix::operator+(const Matrix &m)const
{
	Matrix rep(numRow,numCol);

	for(int i=0;i<numRow;i++)
	{
	
		for(int j=0;j<numCol;j++)
		{
			rep[i][j]=tab[i][j]+m[i][j];
		}
	}

	return rep;

}

//surchage de l'operateur d'affectation
//verifier que la matrice de reception soit à la bonne taille
Matrix Matrix::operator=(const Matrix &m)
{
	numRow=m.getNumRow();

	numCol=m.getNumCol();
		
	for(int i=0;i<numRow;i++)
	{
		for(int j=0;j<numCol;j++)
		{
			tab[i][j]=m[i][j];
		}
	}
	
	
	//permet a=b=c
	return *this;
}


//getteur
int Matrix::getNumCol() const
{
	return numCol;
}

int Matrix::getNumRow() const
{
	return numRow;
}


//Surchage de l'operateur *
//tester la taille
Matrix Matrix::operator*(const Matrix &m) const
{
	Matrix rep(numRow,m.getNumCol());

	for(int i=0;i<numRow;i++)
	{
		for(int j=0;j<m.getNumCol();j++)
		{
			for(int k=0;k<numCol;k++)
			{
				rep[i][j]+=tab[i][k]*m[k][j];
			}
		}
	}

	return rep;
}

//surchage l'operateur[]
//permet d'acceder au valeur matrice[i][j]
double* Matrix::operator[](int numRow_) const
{
  return tab[numRow_];
}



Matrix Matrix::T() const
{
	Matrix rep(numRow,numCol);
	for(int i=0;i<numRow;i++)
	{
		for(int j=0;j<getNumCol();j++)
		{
			rep[j][i]=tab[i][j];
		}
	}
	return rep;
}


//affichage de la matrice
std::ostream& operator<<(std::ostream& s, Matrix& m) {
	for (int i=0; i<m.getNumRow(); i++)  {
		for (int j=0; j<m.getNumCol(); j++) {
			s << m[i][j];
			if (j<m.getNumCol()-1) s << "\t";
		}
		s << std::endl;
	}
	return s;
}

//the X axis rotation matrix 
Matrix Matrix::rotX(double delta) 
{
	Matrix rep(4,4);
	 
	rep.fill(	1.,		0.,		0.,		0.,
			0.,		cos(delta),	-sin(delta),	0.,
			0.,		sin(delta),	cos(delta),	0.,
			0.,		0.,		0.,		1.);
			
	return rep;
}

//the Y axis rotation matrix
Matrix Matrix::rotY(double delta)
{
	Matrix rep(4,4);
	
	rep.fill(	cos(delta),	0.,		sin(delta),	0.,
			0.,		1.,		0.,		0.,	
			-sin(delta),	0.,		cos(delta),	0.,
			0.,		0.,		0.,		1.);
			
	return rep;
}

//the Z axis rotation matrix
Matrix Matrix::rotZ(double delta)
{
	Matrix rep(4,4);
	
	
	rep.fill(	cos(delta),	-sin(delta),	0.,		0.,
			sin(delta),	cos(delta),	0.,		0.,
			0.,		0.,		1.,		0.,
			0.,		0.,		0.,		1.);
			
	return rep;	
	
}

Matrix  Matrix::translation(double x,double y,double z)
{
	Matrix rep(4,4);
	
	
	rep.fill(	1.,		0.,		0.,		0.,
			0.,		1.,		0.,		0.,
			0.,		0.,		1.,		0.,
			x,		y,		z,		1.);
			
	return rep;	
}

Matrix  Matrix::projection(double z)
{
	Matrix rep(4,4);
	
	
	rep.fill(	1.,		0.,		0.,		0.,
			0.,		1.,		0.,		0.,
			0.,		0.,		0.,		-1./z,
			0.,		0.,		0.,		1.);
			
	return rep;	
}

Matrix Matrix::compute_translation_and_rotations(double xPlan,double yPlan,double zPlan,double xCam,double yCam,double zCam)
{
	//here i calculate the vector coordinates that must be put on the z axis
	double xVect=xCam-xPlan;
	double yVect=yCam-yPlan;
	double zVect=zCam-zPlan;
  
	
	//i give initialized values to the cosinus and sinus of the two angle of rotations (rotation on x axis and on y axis)
	double cosRotX=1.;
	double sinRotX=0.;
	double cosRotY=1.;
	double sinRotY=0.;
	
	
	//if the vector is on the x axis, there's no need of the X axis rotation  
	if(zVect==0.&&yVect==0.)
	{
		cosRotY=0.;
		sinRotY=1.0;
		
	}
	else  //else i calculate the cosinus and sinus of the X axis rotation
	{
		cosRotX = zVect/sqrt(zVect*zVect+yVect*yVect) ;
		sinRotX = yVect/sqrt(zVect*zVect+yVect*yVect) ;
		
	}
	
	
	//the vector is nul, there is no need for rotations
	if(zVect==0.&&yVect==0.&&zVect==0.)
	{
		cosRotY=1.;
		sinRotY=0.;
	  
	}
	else //else i calculate cosinus and sinus of the Y axis rotation
	{
		cosRotY = sqrt(zVect*zVect+yVect*yVect)/sqrt(xVect*xVect+yVect*yVect+zVect*zVect) ;
		sinRotY = -xVect/sqrt(xVect*xVect+yVect*yVect+zVect*zVect) ;
	}

		
	Matrix rotX(4,4);
	rotX.fill(	1.		,0.		,0.		,0.,
			0.		,cosRotX	,-sinRotX	,0.,
			0.		,sinRotX	,cosRotX	,0.,
			0.		,0.		,0.		,1.);
			
	Matrix rotY(4,4);
	rotY.fill(	cosRotY		,0.		,sinRotY	,0.,
			0.		,1.		,0.		,0.,
			-sinRotY	,0.		,cosRotY	,0.,
			0.		,0.		,0.		,1.);
			
			
	// i do the Matrix multiplication
	Matrix translation=Matrix::translation(-xPlan,-yPlan,-zPlan);
	
	return translation*rotX*rotY;
}

//this function calculate the rotation matrix when the arcball returns a vector (normalized to make its length be the angle) to rotate around
Matrix Matrix::computeArcballRotation(double xRot,double yRot,double zRot)
{
	double alpha = sqrt(xRot*xRot+yRot*yRot+zRot*zRot) ;
  
	//i give initialized values to the cosinus and sinus of the two angle of rotations (rotation on x axis and on y axis)
	double cosRotX=1.;
	double sinRotX=0.;
	double cosRotY=1.;
	double sinRotY=0.;
	
	
	if(zRot==0.&&yRot==0.)//if the vector is on the x axis, there's no need of the X axis rotation  
	{
		cosRotY=0.;
		sinRotY=1.0;
		
	}
	else //else i calculate the cosinus and sinus of the X axis rotation
	{
		cosRotX = zRot/sqrt(zRot*zRot+yRot*yRot) ;
		sinRotX = yRot/sqrt(zRot*zRot+yRot*yRot) ;
		
	}
	//the vector is nul, there is no need for rotations
	if(zRot==0.&&yRot==0.&&zRot==0.)
	{
	  
		cosRotY=1.;
		sinRotY=0.;
	}
	else//else i calculate cosinus and sinus of the Y axis rotation
	{
		cosRotY = sqrt(zRot*zRot+yRot*yRot)/sqrt(xRot*xRot+yRot*yRot+zRot*zRot) ;
		sinRotY = -xRot/sqrt(xRot*xRot+yRot*yRot+zRot*zRot) ;
	}

	// i calculate the three rotation matrix
	Matrix rotX(4,4);
	rotX.fill(	1.		,0.		,0.		,0.,
			0.		,cosRotX	,-sinRotX	,0.,
			0.		,sinRotX	,cosRotX	,0.,
			0.		,0.		,0.		,1.);
			
	Matrix rotY(4,4);
	rotY.fill(	cosRotY		,0.		,sinRotY	,0.,
			0.		,1.		,0.		,0.,
			-sinRotY	,0.		,cosRotY	,0.,
			0.		,0.		,0.		,1.);
			
	Matrix rotZ=Matrix::rotZ(alpha);

	
	//the final rotation matrix is composed with the three first rotations + the transpose of the X and Y rotations matrix .
	return rotX*rotY*rotZ*rotY.T()*rotX.T();
  
}


/*
Matrix Matrix::matrixRotateEuler(vector<double> v){	//matrice d'euler
	double A,B,C,D,E,F,AD,BD;

	A = cos(v.x);
	B = sin(v.x);
	C = cos(v.y);
	D = sin(v.y);
	E = cos(v.z);
	F = sin(v.z);

	AD = A*D;
	BD = B*D;
		
	m[0][0] = C*E;
	m[0][1] = -C*F;
	m[0][2] = -D;

	m[1][0] = -BD * E + A * F;
	m[1][1] = BD * E + A * F;
	m[1][2] = -B * C;

	m[2][0] = AD * E + B * F;
	m[2][1] = -AD * E + B * F;
	m[2][2] = A * C;

	m[0][3] = m[1][3] = m[2][3] = m[3][0] = m[3][1] = m[3][2] = 0;
	m[3][3] = 1;
	

	return 	Matrix (4,4,
		C*E,			-C*F,			-D,			0.,
		-BD * E + A *F,	BD * E + A * F,	-B * C	,	0.,
		AD * E + B * F,	-AD * E + B * F,A * C	,	0.,
		0.		 		,0.,				0.,			1.);
}
*/

